package com.xiyuan.smartutils.seqs;

import com.xiyuan.smartutils.*;
import com.xiyuan.smartutils.constants.CodeConstants;

import java.io.RandomAccessFile;

/**
 * 整型序列值，默认从1从0x7FFFFFFF(最大值)，并支持保存文件<br>
 * <br>
 * 1、最小值必须>=1<br>
 * 2、如果设置最大值，则最大值必须大小最小值，否则取整数最大值0x7FFFFFFF<br>
 * 3、是否自动保存
 *
 * @version v1.0.0 @author lgz 2017-8-27 新建与整理
 */
public class SequenceFile extends Sequence implements CodeConstants {
    private final boolean auto;
    private final String filePath;
    private final RandomAccessFile file;
    private final boolean isValid;
    
    /***********************************************************************************/
    // 默认自动加载
    /***********************************************************************************/
    
    /**
     * 从1到0x7FFFFFFF，自动加载
     */
    public SequenceFile(String filePath) {
        this(Ints.length(Integer.MAX_VALUE), filePath);
    }
    
    /**
     * 从1到len个9，自动加载
     */
    public SequenceFile(int len, String filePath) {
        this(1, Ints.getMaxInt(len), 1, filePath, true);
    }
    
    /**
     * 从min到max，自动加载
     */
    public SequenceFile(long min, long max, String filePath) {
        this(min, max, filePath, true);
    }
    
    /**
     * 从min到max，指定初始值，自动加载
     */
    public SequenceFile(long min, long max, long seq, String filePath) {
        this(min, max, seq, filePath, true);
        
    }
    
    /***********************************************************************************/
    // 设置自动/手动加载，一般用于手动加载
    /***********************************************************************************/
    
    /**
     * 从1到0x7FFFFFFF
     */
    public SequenceFile(String filePath, boolean auto) {
        this(Ints.length(Integer.MAX_VALUE), filePath, auto);
    }
    
    /**
     * 从1到指定长度的len个19
     */
    public SequenceFile(int len, String filePath, boolean auto) {
        this(1, Longs.getMax(len), filePath, auto);
    }
    
    /**
     * 从min到max
     */
    public SequenceFile(long min, long max, String filePath, boolean auto) {
        this(min, max, min, filePath, auto);
    }
    
    /***********************************************************************************/
    // 全属性构造函数
    /***********************************************************************************/
    
    /**
     * 本类和子类可见
     */
    protected SequenceFile(long min, long max, long seq, String filePath, boolean auto) {
        super(min, max, seq);
        
        this.filePath = filePath;
        this.auto = auto;
        
        String fileDir = Files.getFileDir(filePath);
        if (!Files.mkdir(fileDir)) {// 检查文件路径失败时不抛异常，防止NoClassDefineError
            file = null;
            isValid = false;
        }
        else {
            // 文件正常，加载初始值
            file = RAFiles.newFileRW(this.filePath);
            if (auto) {
                // 自动加载，=false时，通过new SequenceFile(10, filePath, false).load();方式加载
                load();
            }
            isValid = true;
        }
    }
    
    public String getFilePath() {
        return filePath;
    }
    
    public boolean isAuto() {
        return auto;
    }
    
    public boolean isValid() {
        return isValid;
    }
    
    protected String readFile() {
        return Files.readUTF8(filePath);
    }
    
    protected void writeFile(String str) {
        RAFiles.writeUTF8(file, 0, str);
    }
    
    /********************************************************************/
    // 加载和保存序列号，自动标志打开时，新建即加载，每次next即保存
    /********************************************************************/
    
    /**
     * 加载序列号
     */
    public SequenceFile load() {
        if (!Files.isFile(filePath)) {return this;}
        
        String seq = StrUtils.trim(readFile());
        if (!Validates.isIntegerPositive(seq)) {return this;}
        
        setSequence(Longs.toLong(seq));
        return this;
    }
    
    /**
     * 保存序列号
     */
    public void save() {// 19位最大长整型，不足后补空格，保证每次写入时完整
        writeFile(StrUtils.suffixLen(sequence.get(), 19, _SPACE_));
    }
    
    /********************************************************************/
    // 重写获取并保存
    
    /********************************************************************/
    
    @Override
    public long next() {
        if (!isValid) {// 创建就失败，但不在构造函数中报错，而在使用时报异常
            throw Asserts.exception("创建SequenceFile失败，文件目录不存在且创建失败");
        }
        
        if (!auto) {// 无需自动保存
            return super.next();
        }
        
        synchronized (this) {// 自动保存要求保存成功再返回（保存下一个，返回当前）
            chkSequence();
            long seq = sequence.incrementAndGet();
            save();
            return seq;
        }
    }
}
